/*
 * Copyright (c) 2007 T.Cogghe, D.Leroux, S.Lévignac, S.Rives
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#ifndef __MATRICE_H__
#define __MATRICE_H__

#include <iostream>

#include "types.hh"
#include "Astre.hh"

class Vecteur {
private:
	Reel lignes[3];
public:
	/*! construit un vecteur ad hoc */
	Vecteur(const Reel x,const Reel y,const Reel z) {
	lignes[0]=x;
	lignes[1]=y;
	lignes[2]=z;
}

	/*! construit un vecteur à partir d'un astre : (x,y,1) */
	Vecteur(const Astre&a) {
		lignes[0]=a.x();
		lignes[1]=a.y();
		lignes[2]=1.;
	}

	/*! duplique */
	Vecteur(const Vecteur&v) { memcpy(lignes,v.lignes,3*sizeof(Reel)); }

	/*! détruit */
	~Vecteur() {}
	/*! affectation */
	Vecteur&operator=(const Vecteur&v) { memcpy(lignes,v.lignes,3*sizeof(Reel)); return *this; }

	/*! accède à un coefficient */
	const Reel& operator()(const int i) const { return lignes[i]; }
	Reel& operator()(const int i) { return lignes[i]; }
};

/* une matrice 3x3 */
class Matrice {
private:
	/*! trois colonnes de trois lignes */
	Vecteur v1,v2,v3;
	Vecteur* colonnes[3];

public:
	/*! construit une matrice ad hoc */
	Matrice(const Reel a,const Reel b,const Reel c,const Reel d,
		const Reel e,const Reel f,const Reel g,const Reel h,const Reel i)
			: v1(a,b,c), v2(d,e,f), v3(g,h,i),
			  colonnes( (Vecteur*[]) {&v1,&v2,&v3} )
	{}

	/*! construit l'identité */
	Matrice() : v1(1.,0.,0.), v2(0.,1.,0.), v3(0.,0.,1.), colonnes((Vecteur*[]){&v1,&v2,&v3})
	{}

	/*! duplique */
	Matrice(const Matrice&m) : v1(m.col(0)), v2(m.col(1)), v3(m.col(2)),
				  colonnes((Vecteur*[]){&v1,&v2,&v3})
	{}

	/*! construit la matrice à partir de 3 vecteurs colonne */
	Matrice(const Vecteur&a,const Vecteur&b,const Vecteur&c)
		: v1(a), v2(b), v3(c), colonnes((Vecteur*[]){&v1,&v2,&v3})
	{}

	/*! construit la matrice à partir de 3 astres (avec un astre centré sur (x,y), on forme le vecteur colonne (x,y,1) */
	Matrice(const Astre&a,const Astre&b,const Astre&c)
		: v1(a.x(),a.y(),1.), v2(b.x(),b.y(),1.), v3(c.x(),c.y(),1.),
		  colonnes((Vecteur*[]){&v1,&v2,&v3})
	{}

	/*! détruit */
	~Matrice() {}

	/*! accède à un coefficient */
	const Reel& operator() (const int ligne,const int colonne) const { return (*colonnes[colonne])(ligne); }

	Reel& operator() (const int ligne,const int colonne) { return (*colonnes[colonne])(ligne); }

	const class Vecteur&col(const int i) const { return *(colonnes[i]); }



	/*! affectation */
	Matrice&operator=(const Matrice&m) { v1=m.col(0); v2=m.col(1); v3=m.col(2); return *this; }

	/*! dit si la matrice est inversible */
	bool inversible();
	/*! retourne l'inverse de la matrice */
	Matrice inverse() const;
	/*! retourne la transposition de la matrice */
	Matrice transpose() const;
	/*! retourne la trace de la matrice */
	Reel trace() const;
	/*! donne le déterminant de la matrice */
	Reel determinant() const;

	/*! la matrice identité */
	static const Matrice Id;
};

class Matrice operator+(const Matrice&,const Matrice&);
class Matrice operator-(const Matrice&,const Matrice&);
class Matrice operator*(const Matrice&,const Matrice&);
class Matrice operator*(Reel,const Matrice&);

inline class Vecteur operator*(const Matrice&m,const Vecteur&v) {
	return Vecteur(
		v(0)*m(0,0) + v(1)*m(0,1) + v(2)*m(0,2),
		v(0)*m(1,0) + v(1)*m(1,1) + v(2)*m(1,2),
		v(0)*m(2,0) + v(1)*m(2,1) + v(2)*m(2,2));
}

class Vecteur operator*(const Matrice&,const Position&);

bool zero(const Reel);
bool zero(const Matrice&);

std::ostream& operator<<(std::ostream&,const class Matrice&);

#endif
